import { fork, put, select, call, take } from "redux-saga/effects";
import type { RouteChangeActionPayload } from "actions/focusHistoryActions";
import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity";
import log from "loglevel";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import { getRecentEntityIds } from "selectors/globalSearchSelectors";
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import { type ReduxAction } from "actions/ReduxActionTypes";
import { getCurrentThemeDetails } from "selectors/themeSelectors";
import type { BackgroundTheme } from "sagas/ThemeSaga";
import { changeAppBackground } from "sagas/ThemeSaga";
import { updateRecentEntitySaga } from "sagas/GlobalSearchSagas";
import {
  setLastSelectedWidget,
  setSelectedWidgets,
} from "actions/widgetSelectionActions";
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
import FocusRetention from "sagas/FocusRetentionSaga";
import { getSafeCrash } from "selectors/errorSelectors";
import { flushErrors } from "actions/errorActions";
import type { NavigationMethod } from "utils/history";
import UsagePulse from "usagePulse";
import { getIDETypeByUrl } from "ee/entities/IDE/utils";
import type { EditorViewMode } from "IDE/Interfaces/EditorTypes";
import { IDE_TYPE } from "ee/IDE/Interfaces/IDETypes";
import { updateIDETabsOnRouteChangeSaga } from "sagas/IDESaga";
import { getIDEViewMode } from "selectors/ideSelectors";

let previousPath: string;

export function* handleRouteChange(
  action: ReduxAction<RouteChangeActionPayload>,
) {
  const { pathname, state } = action.payload.location;

  try {
    yield fork(clearErrors);
    yield fork(watchForTrackableUrl, action.payload);
    const IDEType = getIDETypeByUrl(pathname);

    if (previousPath) {
      yield fork(
        FocusRetention.onRouteChange.bind(FocusRetention),
        pathname,
        previousPath,
        state,
      );
    }

    if (IDEType === IDE_TYPE.App) {
      yield fork(logNavigationAnalytics, action.payload);
      yield fork(appBackgroundHandler);
      const entityInfo = identifyEntityFromPath(pathname);

      yield fork(updateRecentEntitySaga, entityInfo);
      yield fork(updateIDETabsOnRouteChangeSaga, entityInfo);
      yield fork(setSelectedWidgetsSaga, state?.invokedBy);
    }
  } catch (e) {
    log.error("Error in focus change", e);
  } finally {
    previousPath = pathname;
  }
}

function* appBackgroundHandler() {
  const currentTheme: BackgroundTheme = yield select(getCurrentThemeDetails);

  changeAppBackground(currentTheme);
}

/**
 * When an error occurs, we take over the whole router and keep it the error
 * state till the errors are flushed. By default, we will flush out the
 * error state when a CTA on the page is clicked but in case the
 * user navigates via the browser buttons, this will ensure
 * the errors are flushed
 * */
function* clearErrors() {
  const isCrashed: boolean = yield select(getSafeCrash);

  if (isCrashed) {
    yield put(flushErrors());
  }
}

function* watchForTrackableUrl(payload: RouteChangeActionPayload) {
  yield take([
    ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
    ReduxActionTypes.INITIALIZE_PAGE_VIEWER_SUCCESS,
  ]);
  const oldPathname = payload.prevLocation.pathname;
  const newPathname = payload.location.pathname;
  const isOldPathTrackable: boolean = yield call(
    UsagePulse.isTrackableUrl,
    oldPathname,
  );
  const isNewPathTrackable: boolean = yield call(
    UsagePulse.isTrackableUrl,
    newPathname,
  );

  // Trackable to Trackable URL -> No pulse
  // Non-Trackable to Non-Trackable URL -> No pulse
  // Trackable to Non-Trackable -> No Pulse
  // Non-Trackable to Trackable URL -> Send Pulse

  if (!isOldPathTrackable && isNewPathTrackable) {
    yield call(UsagePulse.sendPulseAndScheduleNext);
  }
}

function* logNavigationAnalytics(payload: RouteChangeActionPayload) {
  const {
    location: { pathname, state },
  } = payload;
  const recentEntityIds: Array<string> = yield select(getRecentEntityIds);
  const currentEntity = identifyEntityFromPath(pathname);
  const previousEntity = identifyEntityFromPath(previousPath);
  const isRecent = recentEntityIds.some(
    (entityId) => entityId === currentEntity.id,
  );
  const ideViewMode: EditorViewMode = yield select(getIDEViewMode);
  const { height, width } = window.screen;

  AnalyticsUtil.logEvent("ROUTE_CHANGE", {
    toPath: pathname,
    fromPath: previousPath || undefined,
    navigationMethod: state?.invokedBy,
    isRecent,
    recentLength: recentEntityIds.length,
    toType: currentEntity.entity,
    fromType: previousEntity.entity,
    screenHeight: height,
    screenWidth: width,
    editorMode: ideViewMode,
  });
}

export function* setSelectedWidgetsSaga(invokedBy?: NavigationMethod) {
  const pathname = window.location.pathname;
  const entityInfo = identifyEntityFromPath(pathname);
  let widgets: string[] = [];
  let lastSelectedWidget = MAIN_CONTAINER_WIDGET_ID;

  if (entityInfo.entity === FocusEntity.WIDGET) {
    widgets = entityInfo.id.split(",");

    if (widgets.length) {
      lastSelectedWidget = widgets[widgets.length - 1];
    }
  }

  yield put(setSelectedWidgets(widgets, invokedBy));
  yield put(setLastSelectedWidget(lastSelectedWidget));
}
